#include "elfread.h"
#include <fstream>
#include <sstream>
#include <vector>
#include <memory>
#include <string.h>
#include <string>
#include <iomanip>
#include <iostream>
#include <elf.h>
#include <QDebug>
ELFRead::ELFRead()
    : fileBuffer(nullptr)

{

}

ELFRead::~ELFRead()
{
    free(fileBuffer);
}

void ELFRead::updateData(std::string fileName)
{
    this->fileName = fileName;
    getFileBuffer();
}

void ELFRead::getFileBuffer()
{
    std::ifstream file(fileName,std::ios::binary| std::ios::ate);
    if(!file.is_open()){
        return;
    }
    std::ifstream::pos_type fileSize = file.tellg();
    file.seekg(0,std::ios::beg);
    std::vector<char> buffer(fileSize);
    if(!file.read(buffer.data(),fileSize)){
        return;
    }
//    if(fileBuffer){
//        free(fileBuffer);
//        fileBuffer = nullptr;
//    }
    fileBuffer = (u_char*)malloc(fileSize);
    if(!fileBuffer){
        return;
    }
    memcpy(fileBuffer,buffer.data(),fileSize);
    file.close();
}

ELF_HEADER ELFRead::getHeaderInfo()
{
    ELF_HEADER res;
    Elf64_Ehdr *  ehdr = ReadFileHead();
    if(!ehdr){
        return res;
    }
    res.strMagicNumber = getMagicNumber(ehdr);
    res.strObjectFileTyoe = getObjectFileType(ehdr);
    res.strArchitecture = getArchitecture(ehdr);
    res.strObjectFileVersion = getObjectFileVersion(ehdr);
    res.strEntryPointVirtualAddress = getEntryPointVirtualAddress(ehdr);
    res.strProgramHeaderOffset = getProgramHeaderOffset(ehdr);
    res.strSectionHeaderOffset = getSectionHeaderOffset(ehdr);
    res.strProcessorSpecificFlags = getProcessorSpecificFlags(ehdr);
    res.strElfHeaderSize = getElfHeaderSize(ehdr);
    res.strProgramHeaderSize = getProgramHeaderSize(ehdr);
    res.strProgramHeaderNumber = getProgramHeaderNumber(ehdr);
    res.strSectionHeaderSize = getSectionHeaderSize(ehdr);
    res.strSectionHeaderNumber = getSectionHeaderNumber(ehdr);
    res.strSectionHeaderStringTableIndex = getSectionHeaderStringTableIndex(ehdr);
    //ReadFileSectionHeader();
    return res;
}

std::string ELFRead::getMagicNumber(Elf64_Ehdr *ehdr)
{
    std::string res = "";
    for(int i = 0 ; i <EI_NIDENT ; i++){
        std::stringstream ss;
        ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(ehdr->e_ident[i]);
        res += ss.str();
    }
    return res;
}

std::string ELFRead::getObjectFileType(Elf64_Ehdr *ehdr)
{
    std::string res = "";
    switch (ehdr->e_type) {
    case ET_NONE:
        res = "No file type";
        break;
    case ET_REL:
        res = "Relocationtable file";
        break;
    case ET_EXEC:
        res = "Executable file";
        break;
    case ET_DYN:
        res = "Shared object file";
        break;
    case  ET_CORE:
        res = "Core file";
        break;
    }
    return res;
}

std::string ELFRead::getArchitecture(Elf64_Ehdr *ehdr)
{
    std::string res = "";
    switch(ehdr->e_machine){
    case EM_386:
        res = "Intel 80386";
        break;
    case EM_ARM:
        res = "ARM";
        break;
    case EM_X86_64:
        res = "AMD X86-64 arrchitecture";
        break;
    default:
        break;
    }
    return res;
}

std::string ELFRead::getObjectFileVersion(Elf64_Ehdr *ehdr)
{
    std::string res = "";
    res = std::to_string( ehdr->e_version);
    return res;
}

std::string ELFRead::getEntryPointVirtualAddress(Elf64_Ehdr *ehdr)
{
    std::string res;
    std::stringstream ss;
    ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(ehdr->e_entry);
    res += "0x"+ ss.str();
    return res;
}

std::string ELFRead::getProgramHeaderOffset(Elf64_Ehdr *ehdr)
{
    std::string res = std::to_string(ehdr->e_phoff);
    return res;
}

std::string ELFRead::getSectionHeaderOffset(Elf64_Ehdr *ehdr)
{
   std::string res = std::to_string(ehdr->e_shoff);
    return res;
}

std::string ELFRead::getProcessorSpecificFlags(Elf64_Ehdr *ehdr)
{
    std::string res = "";
    return res;
}

std::string ELFRead::getElfHeaderSize(Elf64_Ehdr *ehdr)
{

    return std::to_string(ehdr->e_ehsize);
}

std::string ELFRead::getProgramHeaderSize(Elf64_Ehdr *ehdr)
{
    std::string res = std::to_string(ehdr->e_phentsize);
    return res;
}

std::string ELFRead::getProgramHeaderNumber(Elf64_Ehdr *ehdr)
{
    std::string res = std::to_string(ehdr->e_phnum);
    return res;
}

std::string ELFRead::getSectionHeaderSize(Elf64_Ehdr *ehdr)
{
    std::string res = std::to_string(ehdr->e_shentsize);
    return res;
}

std::string ELFRead::getSectionHeaderNumber(Elf64_Ehdr *ehdr)
{
     std::string res = std::to_string(ehdr->e_shnum);
    return res;
}

std::string ELFRead::getSectionHeaderStringTableIndex(Elf64_Ehdr *ehdr)
{
    std::string res = std::to_string(ehdr->e_shstrndx);
    return res;
}

Elf64_Ehdr * ELFRead::ReadFileHead()
{
    Elf64_Ehdr * ehdr = (Elf64_Ehdr * )fileBuffer;
    return ehdr;
}

 std::vector<ELF_SHDR> ELFRead::ReadFileSectionHeader()
{
     Elf64_Ehdr *  ehdr = ReadFileHead();
    std::vector<ELF_SHDR>  res;

    Elf64_Shdr *shdr = (Elf64_Shdr *)(fileBuffer + ehdr->e_shoff);
    for(int i  = 0 ;i < ehdr->e_shnum;i++){
         Elf64_Shdr item =  (Elf64_Shdr )shdr[i];
         ELF_SHDR itemInfo;
         itemInfo.strSectionName = getSectionName(item,ehdr);
         itemInfo.strSectionType = getSectionType(item);
         itemInfo.strSectionFlags = getSectionFlags(item);
         itemInfo.strSectionVirtualAddr = getSectionVirtualAddr(item);
         itemInfo.strSectionOffset = getSectionOffset(item);
         itemInfo.strSectionSize = getSectionSize(item);
         itemInfo.strSectionLink = getSectionLink(item);
         itemInfo.strSectionInfo = getSectionInfo(item);
         itemInfo.strSectionAlignment = getSectionAlignment(item);
         itemInfo.strSectionEntrySize = getSectionEntrySize(item);
         res.push_back(itemInfo);
        // qInfo() <<i << itemInfo.strSectionFlags.c_str()  <<" " << itemInfo.strSectionType.c_str() <<" "  << itemInfo.strSectionName.c_str();
         SHT_NULL;
    }
    return res;
}

std::string ELFRead::getSectionName(const Elf64_Shdr item,const Elf64_Ehdr * ehdr )
{
    Elf64_Shdr *shdr = reinterpret_cast<Elf64_Shdr *>(fileBuffer + ehdr->e_shoff);
    char *strtab = reinterpret_cast<char *>(fileBuffer + shdr[ehdr->e_shstrndx].sh_offset);
    std::string res(strtab + item.sh_name);
    return res;
}
#define SHT_CASE_TYPE(XX) case SHT_##XX: res=#XX;break;
std::string ELFRead::getSectionType(Elf64_Shdr item)
{
    std::string res = "";
    switch(item.sh_type){
        SHT_CASE_TYPE(NULL)
        SHT_CASE_TYPE(PROGBITS)
        SHT_CASE_TYPE(SYMTAB)
        SHT_CASE_TYPE(STRTAB)
        SHT_CASE_TYPE(RELA)
        SHT_CASE_TYPE(HASH)
        SHT_CASE_TYPE(DYNAMIC)
        SHT_CASE_TYPE(NOTE)
        SHT_CASE_TYPE(NOBITS)
        SHT_CASE_TYPE(REL)
        SHT_CASE_TYPE(SHLIB)
        SHT_CASE_TYPE(DYNSYM)
        SHT_CASE_TYPE(INIT_ARRAY)
        SHT_CASE_TYPE(FINI_ARRAY)
        SHT_CASE_TYPE(PREINIT_ARRAY)
        SHT_CASE_TYPE(GROUP)
        SHT_CASE_TYPE(SYMTAB_SHNDX)
        SHT_CASE_TYPE(NUM)
        SHT_CASE_TYPE(LOOS)
        SHT_CASE_TYPE(GNU_ATTRIBUTES)
        SHT_CASE_TYPE(GNU_HASH)
        SHT_CASE_TYPE(GNU_LIBLIST)
        SHT_CASE_TYPE(CHECKSUM)
        SHT_CASE_TYPE(LOSUNW)
        SHT_CASE_TYPE(SUNW_COMDAT)
        SHT_CASE_TYPE(SUNW_syminfo)
        SHT_CASE_TYPE(GNU_verdef)
        SHT_CASE_TYPE(GNU_verneed)
        SHT_CASE_TYPE(GNU_versym)
        SHT_CASE_TYPE(LOPROC)
        SHT_CASE_TYPE(HIPROC)
        SHT_CASE_TYPE(LOUSER)
        SHT_CASE_TYPE(HIUSER)
    }


    return res;
}

std::string ELFRead::getSectionFlags(Elf64_Shdr item)
{
    std::string res = "";
    if(item.sh_flags & SHF_WRITE){
        res += "W";
    }
    if(item.sh_flags & SHF_ALLOC){
        res += "A";
    }
    if(item.sh_flags & SHF_EXECINSTR){
        res += "X";
    }
    if(item.sh_flags & SHF_MERGE){
        res += "M";
    }
    if(item.sh_flags & SHF_STRINGS){
        res += "S";
    }
    if(item.sh_flags & SHF_INFO_LINK){
        res += "S";
    }
    if(item.sh_flags & SHF_LINK_ORDER){
        res += "S";
    }
    if(item.sh_flags & SHF_OS_NONCONFORMING){
        res += "O";
    }
    if(item.sh_flags & SHF_GROUP){
        res += "G";
    }
    if(item.sh_flags & SHF_TLS){
        res += "T";
    }
    if(item.sh_flags & SHF_COMPRESSED){
        res += "C";
    }
    if(item.sh_flags & SHF_MASKOS){
        res += "o";
    }
    if(item.sh_flags & SHF_MASKPROC){
        res += "P";
    }
    return res;
}

std::string ELFRead::getSectionVirtualAddr(Elf64_Shdr item)
{
    return getAddr(item.sh_addr);
}

std::string ELFRead::getSectionOffset(Elf64_Shdr item)
{
    std::string res = std::to_string(item.sh_offset);
    return res;
}

std::string ELFRead::getSectionSize(Elf64_Shdr item)
{
    std::string res = std::to_string(item.sh_size);
    return res;
}

std::string ELFRead::getSectionLink(Elf64_Shdr item)
{
    return getAddr(item.sh_link);
}

std::string ELFRead::getSectionInfo(Elf64_Shdr item)
{
    return getAddr(item.sh_info);
}

std::string ELFRead::getSectionAlignment(Elf64_Shdr item)
{
    return getAddr(item.sh_addralign);
}

std::string ELFRead::getSectionEntrySize(Elf64_Shdr item)
{
    std::string res = std::to_string(item.sh_entsize);
    return res;
}

#define PT_CASE_TYPE(XX) case PT_##XX: res=#XX;break;
std::string ELFRead::getSegmentType(Elf64_Phdr item)
{
    std::string res="";
    switch (item.p_type) {
        PT_CASE_TYPE(NULL);
        PT_CASE_TYPE(LOAD);
        PT_CASE_TYPE(DYNAMIC);
        PT_CASE_TYPE(INTERP);
        PT_CASE_TYPE(NOTE);
        PT_CASE_TYPE(SHLIB);
        PT_CASE_TYPE(PHDR);
        PT_CASE_TYPE(TLS);
        PT_CASE_TYPE(NUM);
        PT_CASE_TYPE(LOOS);
        PT_CASE_TYPE(GNU_EH_FRAME);
        PT_CASE_TYPE(GNU_STACK);
        PT_CASE_TYPE(GNU_RELRO);
        PT_CASE_TYPE(GNU_PROPERTY);
        PT_CASE_TYPE(SUNWBSS);
        PT_CASE_TYPE(SUNWSTACK);
        PT_CASE_TYPE(HISUNW);
        PT_CASE_TYPE(LOPROC);
        PT_CASE_TYPE(HIPROC);
    }
    return res;
}

std::string ELFRead::getSegmentOffset(Elf64_Phdr item)
{
    std::string res = std::to_string(item.p_offset);
    return res;
}

std::string ELFRead::getSegmentVaddr(Elf64_Phdr item)
{
    return getAddr(item.p_vaddr);
}

std::string ELFRead::getSegmentPaddr(Elf64_Phdr item)
{
    return getAddr(item.p_paddr);
}

std::string ELFRead::getSegmentFilesz(Elf64_Phdr item)
{
    return std::to_string(item.p_filesz);
}

std::string ELFRead::getSegmentMemsz(Elf64_Phdr item)
{
    return std::to_string(item.p_memsz);
}

std::string ELFRead::getSegmentFlags(Elf64_Phdr item)
{
    std::string res = "";
    if(item.p_flags & PF_X){
        res += "X";
    }
    if(item.p_flags & PF_W){
        res += "W";
    }
    if(item.p_flags & PF_R){
        res += "R";
    }
    if(item.p_flags & PF_MASKOS){
        res += "O";
    }
    if(item.p_flags & PF_MASKPROC){
        res += "P";
    }
    return std::to_string(item.p_flags);
}

std::string ELFRead::getSegmentAlign(Elf64_Phdr item)
{
    return getAddr(item.p_align);
}

std::vector<ELF_PHDR> ELFRead::getFileProgramHeader()
{
    Elf64_Ehdr *  ehdr = ReadFileHead();
    std::vector<ELF_PHDR> res;
    Elf64_Phdr *phdr =(Elf64_Phdr *)(fileBuffer + ehdr->e_phoff);
    for(int i = 0 ; i < ehdr->e_phnum ; i++){
        Elf64_Phdr item = phdr[i];
        ELF_PHDR itemInfo ;
        itemInfo.strSegmentType = getSegmentType(item);
        itemInfo.strSegmentOffset = getSegmentOffset(item);
        itemInfo.strSegmentVaddr = getSegmentVaddr(item);
        itemInfo.strSegmentPaddr = getSegmentPaddr(item);
        itemInfo.strSegmentFilesz = getSegmentFilesz(item);
        itemInfo.strSegmentMemsz = getSegmentMemsz(item);
        itemInfo.strSegmentFlags = getSegmentFlags(item);
        res.push_back(itemInfo);
    }
    return res;
}

Elf64_Shdr ELFRead::getSection(int index)
{
   Elf64_Ehdr *  ehdr = ReadFileHead();


   Elf64_Shdr *shdr = (Elf64_Shdr *)(fileBuffer + ehdr->e_shoff);
   return shdr[index];
}

unsigned char *ELFRead::getBuffer()
{
    return fileBuffer;
}
std::string ELFRead::getAddr(Elf64_Addr addr)
{
    std::string res;
    std::stringstream ss;
    ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(addr);
    res += "0x"+ ss.str();
    return res;
}
